package org.byd.dmsja.utils;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 带参SQL语句处理类。主要用来弥补PreparedStatement中按参数顺序设置参数值的缺陷。
 */
public class SNEDbSql {

    // myParseResult
    private CParseResult myParseResult = new CParseResult();

    /**
     * 构造函数。
     */
    public SNEDbSql(String namedSql) {
        this.myParseResult = this.priParseNamedSql(namedSql);
    }

    /**
     * 创建。
     */
    public static SNEDbSql create(String namedSql) {
        return new SNEDbSql(namedSql);
    }

    // priParseNamedSql
    private CParseResult priParseNamedSql(String nameSql) {
        var ret = new CParseResult();
        var nameSqlEx = Objects.isNull(nameSql) ? "" : nameSql;
        var rgx = Pattern.compile("([@]{1,2})(\\w+)");
        var ma = rgx.matcher(nameSqlEx);
        ret.questionMarkSql = ma.replaceAll(mr -> {
            var isOkMark = mr.group(1).equals("@");
            if (isOkMark) {ret.paraList.add(CParseParaItem.create(ret.paraList.size() + 1, mr.group(2)));}
            return isOkMark ? "?" : mr.group();
        });
        ret.questionMarkSql = ret.questionMarkSql.replaceAll("@@", "@");
        return ret;
    }

    /**
     * 根据参数名称返回第一个参数顺序号列表。
     */
    public Integer getFirstParaNoListByName(String paraName) {
        var ret = this.myParseResult.paraList.stream()
            .filter(x -> Objects.equals(x.paraName.toLowerCase(), SNE.blankElse(paraName, "").toUpperCase()))
            .map(x -> x.paraNo)
            .findFirst().orElse(0);
        return ret;
    }

    /**
     * 根据参数名称返回对应顺序号（可能有多个）列表。
     */
    public List<Integer> getParaNoListByName(String paraName) {
        var ret = this.myParseResult.paraList.stream()
            .filter(x -> Objects.equals(x.paraName.toLowerCase(), SNE.blankElse(paraName, "").toUpperCase()))
            .map(x -> x.paraNo)
            .collect(Collectors.toList());
        return ret;
    }

    /**
     * 获取具名SQL语句处理结果。
     */
    public CParseResult getParseResult() {
        return this.myParseResult;
    }

    /**
     * 返回问号类型的SQL语句。
     */
    public String getQuestionMarkSql() {
        return this.myParseResult.questionMarkSql;
    }

    /**
     * 使用命名参数。如果一个命名参数对应语句的多个地方，会自动多次调用传入的callback。
     */
    public void useName(String paraName, IUseNameCallback callback) throws Exception {
        try {
            var paraNameEx = paraName.startsWith("@") ?
                paraName.substring(1) : paraName;
            var paraList = this.myParseResult.paraList.stream()
                .filter(x -> x.paraName.equalsIgnoreCase(paraNameEx))
                .collect(Collectors.toList());
            for (var paraNameItem : paraList) {
                callback.execute(paraNameItem.paraNo);
            }
        }
        catch (Exception ex) {
            throw new Exception(SNE.getErrMsg(ex));
        }
    }

    /**
     * batchSetParaByEntity - 使用实体类示例批量设置PreparedStatement的参数值。
     */
    public <TParaEntity> void batchSetParaByEntity(
        PreparedStatement ps, TParaEntity paraEntity) throws SQLException {
        if (ps != null && paraEntity != null) {
            var clsTParaEntity = paraEntity.getClass();
            var parseResult = this.getParseResult();
            try {
                for (var paraItem : parseResult.paraList) {
                    var paraName = paraItem.paraName;
                    var paraNo = paraItem.paraNo;
                    var field = clsTParaEntity.getField(paraName);
                    var paraValue = field.get(paraEntity);
                    ps.setObject(paraNo, paraValue);
                }
            }
            catch (Exception ex) {
                throw new SQLException(ex);
            }
        }
    }

    /**
     * batchSetParaByEntity - 使用Map批量设置PreparedStatement的参数值。
     */
    public void batchSetParaByMap(PreparedStatement ps, Map<String, Object> paraMap) throws SQLException {
        if (ps != null && paraMap != null) {
            var parseResult = this.getParseResult();
            try {
                for (var paraItem : parseResult.paraList) {
                    var paraName = paraItem.paraName;
                    var paraNo = paraItem.paraNo;
                    var paraValue = paraMap.get(paraName);
                    ps.setObject(paraNo, paraValue);
                }
            }
            catch (Exception ex) {
                throw new SQLException(ex);
            }
        }
    }

    /**
     * 设置参数时的回调函数接口定义。
     */
    public interface IUseNameCallback {
        /**
         * paraNo - 对应名称的顺序号（从1开始）。
         */
        void execute(int paraNo) throws Exception;
    }

    /**
     * 具名SQL语句处理结果。
     */
    public static class CParseResult {
        /**
         * 参数列表。
         */
        public List<CParseParaItem> paraList = new ArrayList<>();
        /**
         * 带问号的SQL语句。
         */
        public String questionMarkSql = "";

        /**
         * 创建CParseResult实例。
         */
        public static CParseResult create(List<CParseParaItem> paraList, String questionMarkSql) {
            var ret = new CParseResult();
            ret.paraList = paraList;
            ret.questionMarkSql = questionMarkSql;
            return ret;
        }
    }

    /**
     * 参数项定义。
     */
    public static class CParseParaItem {
        /**
         * 参数顺序号。
         */
        public Integer paraNo = 1;
        /**
         * 参数名称。
         */
        public String paraName = "";

        /**
         * 创建CParseParaItem实例。
         */
        public static CParseParaItem create(Integer paraNo, String paraName) {
            var ret = new CParseParaItem();
            ret.paraNo = paraNo;
            ret.paraName = paraName;
            return ret;
        }
    }
}
